<?php

namespace App\Console\Commands;

use App\Services\SmsService;
use App\SmsTask;
use DB;
use Illuminate\Console\Command;

class ExecuteSmsTasks extends Command
{
    protected $signature = 'execute-sms-tasks';

    protected $description = 'Command description';

    protected $maxTasks = 1000;
    protected $maxTries = 3;

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
        $this->smsSvc = new SmsService();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $this->log('Command started');

        $taskCount = 0;

        while (true) {

            $task = null;

            $this->log("Get retriable task");
            $task = $this->getRetriableTask();
            if ($task) {
                $this->log('Got retriable task');
            } else {
                $this->log("Get new task");
                $task = $this->getNewTask();
                if ($task) {
                    $this->log('Got new task');
                }
            }

            if (empty($task)) {
                $this->log('No more task');
                break;
            }

            $taskCount += 1;
            $this->log("Executing #$taskCount task, record id: {$task->id}");

            $success = $this->smsSvc->sendTemplateSms($task->phone_number, $task->tpl_code, $task->tpl_param);
            if ($success) {
                $this->log('Success');
                $task->update(['result' => 'success']);
            } else {
                $errorMessage = $this->smsSvc->getLastErrorMessage();
                $this->log('Failed: '.$errorMessage);
                $task->update(['result' => $errorMessage]);
            }

            if ($taskCount >= $this->maxTasks) {
                $this->log("Reached max tasks: {$this->maxTasks}");
                break;
            }

        }

        $this->log('Command finished');
    }

    /*
     * 新任务
     */
    private function getNewTask()
    {
        $task = null;
        DB::transaction(function () use (&$task) {
            $task = SmsTask::where('tries', 0)
                ->lockForUpdate()
                ->first();
            if ($task) {
                $task->update([
                    'tries' => $task->tries + 1,
                    'last_tried_at' => now(),
                ]);
            }
        });
        return $task;
    }

    /*
     * 失败任务重试, 两次重试应相隔5分钟
     */
    private function getRetriableTask()
    {
        $task = null;
        DB::transaction(function () use (&$task) {
            $task = SmsTask::where('tries', '>', 0)
                ->where('tries', '<', $this->maxTries)
                ->where('result', '<>', 'success')
                ->where('last_tried_at', '<', now()->addMinutes(-5))
                ->lockForUpdate()
                ->first();
            if ($task) {
                $task->update([
                    'tries' => $task->tries + 1,
                    'result' => null,
                    'last_tried_at' => now(),
                ]);
            }
        });
        return $task;
    }

    private function log($msg)
    {
        $now = now();
        $line = "[$now]$msg";
        $this->line($line);
    }
}
